package pl.enigmatic.util;

import java.util.Iterator;
import java.util.Set;

import pl.enigmatic.math.BijectionMap;
import pl.enigmatic.math.MathUtil;


public class Alphabet {

	public static final char a = 'a';
	public static final char b = 'b';
	public static final char c = 'c';
	public static final char d = 'd';
	public static final char e = 'e';
	public static final char f = 'f';
	public static final char g = 'g';
	public static final char h = 'h';
	public static final char i = 'i';
	public static final char j = 'j';
	public static final char k = 'k';
	public static final char l = 'l';
	public static final char m = 'm';
	public static final char n = 'n';
	public static final char o = 'o';
	public static final char p = 'p';
	public static final char q = 'q';
	public static final char r = 'r';
	public static final char s = 's';
	public static final char t = 't';
	public static final char u = 'u';
	public static final char v = 'v';
	public static final char w = 'w';
	public static final char x = 'x';
	public static final char y = 'y';
	public static final char z = 'z';

	public static final char A = 'A';
	public static final char B = 'B';
	public static final char C = 'C';
	public static final char D = 'D';
	public static final char E = 'E';
	public static final char F = 'F';
	public static final char G = 'G';
	public static final char H = 'H';
	public static final char I = 'I';
	public static final char J = 'J';
	public static final char K = 'K';
	public static final char L = 'L';
	public static final char M = 'M';
	public static final char N = 'N';
	public static final char O = 'O';
	public static final char P = 'P';
	public static final char Q = 'Q';
	public static final char R = 'R';
	public static final char S = 'S';
	public static final char T = 'T';
	public static final char U = 'U';
	public static final char V = 'V';
	public static final char W = 'W';
	public static final char X = 'X';
	public static final char Y = 'Y';
	public static final char Z = 'Z';

	/**
	 * tablica małych liter alfabetu łacińskiego (<b>L</b>ower case <b>L</b>ist)
	 */
	public static final char[] abcLL = { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z };
	/**
	 * tablica małych liter alfabetu łacińskiego (<b>U</b>pper case <b>L</b>ist)
	 */
	public static final char[] abcLU = { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z };
	/**
	 * łąńcuch małych liter alfabetu łacińskiego (<b>L</b>ower case <b>S</b>tring)
	 */
	public static final String abcSL = "abcdefghijklmnopqrstuvwxyz";
	/**
	 * łąńcuch małych liter alfabetu łacińskiego (<b>U</b>pper case <b>S</b>tring)
	 */
	public static final String abcSU = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	public static final int LETTERS_NUM = abcLL.length;

	private static final BijectionMap<Character, Character> next = new BijectionMap<Character, Character>();

	static {
		for (int i = 0; i < abcLL.length; i++) {
			next.putXY(abcLL[i], abcLL[(i + 1) % LETTERS_NUM]);
			next.putXY(abcLU[i], abcLU[(i + 1) % LETTERS_NUM]);
		}
	}

	/**
	 * znajduje numer litery od 0 do 25, gdy parametrwm jest litera z alfabetu łacińskiego (mała, lub wielka)
	 * 
	 * @param let litera, której numer chcemy uzyskać
	 * @return numer litery od 0 do 25 <br>
	 *         -1, gdy parametr nie jest literą
	 * */
	public static int number(final char let) {
		final char tmp = Character.toUpperCase(let);
		final int A = Character.getNumericValue('A');
		final int res = Character.getNumericValue(tmp) - A;
		if (res < 0 || res > 25) { return -1; }
		return res;
	}

	public static int number(final String str) {
		if (str.length() > 0) { return number(str.charAt(0)); }
		return -1;
	}

	/**
	 * zwraca literę, której numerem jest <b>num</b>, przy czym traktuje ten parametr cyklicznie (to znaczy wyciąga z niego wartość modulo 26)
	 * 
	 * @param num numer litery, którą chcemy uzyskać
	 * @return wielka litera alfabetu łacińskiego
	 * */
	public static char letter(final int num) {
		final int tmp = MathUtil.mod(num, LETTERS_NUM);
		return abcLU[tmp];
	}

	public static String letterStr(final int num) {
		return letter(num) + "";
	}

	/**
	 * zwraca napis złożony z liter odpowiadających przekazanemu w parametrze zbiorowi liczb całkowitych
	 * 
	 * @param set zbiór liczb całkowitych
	 * @return napis którego kolejnymi znakami są odpowiadające elementom zbioru <b>set</b> litery
	 * */
	public static String setToStringL(final Set<Integer> set) {
		String res = "";
		final Iterator<Integer> iter = set.iterator();
		while (iter.hasNext()) {
			res += letter(iter.next());
		}
		return res;
	}

	/**
	 * zwraca napis złożony z liczb z przekazanego w parametrze zbioru liczb całkowitych
	 * 
	 * @param set zbiór liczb całkowitych
	 * @return reprezentacja zbioru <b>set</b> w postaci łańcucha znaków
	 * */
	public static String setToStringN(final Set<Integer> set) {
		if (set.isEmpty()) { return "{}"; }
		final Iterator<Integer> iter = set.iterator();
		String res = "{" + Integer.toString(iter.next());
		while (iter.hasNext()) {
			res += "," + Integer.toString(iter.next());
		}
		return res + "}";
	}

	/**
	 * zwraca kolejną literę alfabetu
	 * 
	 * @param let litera alfabetu łacińskiego
	 * @return kolejna po <b>let</b> litera alfabetu
	 * */
	public static char nextLet(final char let) throws Exception {
		return letter(number(let) + 1);
	}

	/**
	 * zwraca poprzednią literę alfabetu
	 * 
	 * @param let litera alfabetu łacińskiego
	 * @return poprzednia przed <b>let</b> litera alfabetu
	 * */
	public static char prevLet(final char let) throws Exception {
		return letter(number(let) + LETTERS_NUM - 1);
	}

	/**
	 * z podanego łańcucha znaków tworzy łańcuch liczb oddzielonych przecinkami, korzystając z przekształcenia <b>Perm.number</b>
	 * 
	 * @param letList łańcuch znaków
	 * @return [Utils.number(letList[0]), ... ]
	 * @see MathUtil.number
	 * */
	public static String strToNumStr(final String letList) {
		if (letList.length() == 0) { return ""; }
		String result = Integer.toString(number(letList.charAt(0)));
		for (int i = 1; i < letList.length(); i++) {
			result += "," + Integer.toString(number(letList.charAt(i)));
		}
		return result;
	}

	public static char next(char let) {
		return next.getY(let);
	}

	public static char prev(char let) {
		return next.getX(let);
	}
}
